/** allegro-test.cpp Tests Allegro's drawing primitives
* \author SP Levitt
* \version 2.0
* \date 2011-08-05
*/

#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5\allegro_native_dialog.h>
#include <string>
#include "colours.h"

using namespace std;

enum SetupStatus
{
	OKAY,
	ALLEGRO_INITIALISATION_ERROR,
	PRIMITIVES_ADDON_ERROR,
	TTF_ADDON_ERROR,
	KEYBOARD_ERROR,
	DISPLAY_ERROR,
	EVENT_QUEUE_ERROR,
	FONT_LOADING_ERROR
};

// pointers to Allegro resources that are used in this program
struct AllegroResources
{
	ALLEGRO_DISPLAY* display;
	ALLEGRO_EVENT_QUEUE* event_queue;
	ALLEGRO_FONT* font;
};

// displays an error message in a dialog box
void displayErrorMessage(const string& message)
{
	al_show_native_message_box(NULL, NULL, NULL, message.c_str(), NULL, ALLEGRO_MESSAGEBOX_ERROR);
}

SetupStatus initialiseAllegroComponents()
{
	// initialise Allegro itself
	if(!al_init()) return ALLEGRO_INITIALISATION_ERROR;

	// initialise primitives addon (for drawing shapes)
	if(!al_init_primitives_addon()) return PRIMITIVES_ADDON_ERROR;

	// initialise font and ttf addon (for drawing text)
	al_init_font_addon();
	if (!al_init_ttf_addon()) return TTF_ADDON_ERROR;

	// install the keyboard
	if(!al_install_keyboard()) return KEYBOARD_ERROR;

	return OKAY;
}

SetupStatus allocateAllegroResources(AllegroResources& resources)
{
	// create the display
	resources.display = al_create_display(640,480);
	if(!resources.display) return DISPLAY_ERROR;

	// create an event queue and register event sources
	resources.event_queue = al_create_event_queue();
	if(!resources.event_queue)
	{
		// cleanup already allocated resources
		al_destroy_display(resources.display);
		return EVENT_QUEUE_ERROR;
	}
	al_register_event_source(resources.event_queue, al_get_keyboard_event_source());
	al_register_event_source(resources.event_queue, al_get_display_event_source(resources.display));

	// load font
	resources.font = al_load_font("data/arial.ttf", 18, 0);
	if (!resources.font)
	{
		// cleanup already allocated resources
		al_destroy_event_queue(resources.event_queue);
		al_destroy_display(resources.display);
		return FONT_LOADING_ERROR;
	}

	return OKAY;
}

SetupStatus setupAllegro(AllegroResources& resources)
{
	SetupStatus initialisation_status = initialiseAllegroComponents();
	if (initialisation_status == OKAY)
		// allocate resources only after successful initialisation
		return allocateAllegroResources(resources);
	else
		return initialisation_status;
}

void reportSetupError(SetupStatus status)
{
	switch (status)
	{
	case ALLEGRO_INITIALISATION_ERROR:
		displayErrorMessage("Failed to initialize Allegro!");
		break;
	case PRIMITIVES_ADDON_ERROR:
		displayErrorMessage("Failed to initialize primitives add-on!");
		break;
	case TTF_ADDON_ERROR:
		displayErrorMessage("Failed to initialize TTF add-on!");
		break;
	case KEYBOARD_ERROR:
		displayErrorMessage("Failed to install keyboard driver!");
		break;
	case DISPLAY_ERROR:
		displayErrorMessage("Failed to create display!");
		break;
	case EVENT_QUEUE_ERROR:
		displayErrorMessage("Failed to create event_queue!");
		break;
	case FONT_LOADING_ERROR:
		displayErrorMessage("A font could not be loaded! Make sure that you copy the 'data' directory with the font file to the directory containing your project file (.vcxproj for Visual C++; .cbp for Code::Blocks).");
		break;
	default:
		//should never reach here
		assert(false);
	}
}

void drawOnDisplay(ALLEGRO_FONT* font)
{
	// clear display
	al_clear_to_color(BLACK);

	// draw a square by colouring individual pixels (slow!)
	for (int row = 100; row < 150; row++)
	{
		for (int column = 100; column < 150; column++)
		{
			al_put_pixel(column, row, al_map_rgb(column, row, 128));
		}
	}

	// Basic usage of Allegro's drawing routines (primitives) is demonstrated by the
	// code below. For more detailed information on using these review the manual
	// which is available at: http://www.allegro.cc/manual/5/primitives.html

	// ellipses
	al_draw_ellipse(150, 150, 20, 30, BLUE, 2);
	al_draw_filled_ellipse(250, 400, 50, 30, YELLOW);

	// circles
	al_draw_circle(210.0,210.0,110.0,BLUE, 6);
	al_draw_filled_circle(425, 125, 20, VIOLET);

	// straight lines
	al_draw_line(350, 100, 470, 450, WHITE, 5);
	al_draw_line(400, 50, 200, 400, RED, 2);

	// spline
	float points[12] = { 125,350, 400,600, 400,450, 50,200, 20,150, 50,100 };
	al_draw_spline(points, TURQUOISE, 2);

	// text
	al_draw_text(font, GREEN, 10, 12, 0, "Allegro-5.0.3 is working. You should see some shapes below.");

	// update display
	al_flip_display();
}

void waitForUserToExit(ALLEGRO_EVENT_QUEUE* event_queue)
{
	while(true) // loop continuously waiting for the user to exit the program
	{
		ALLEGRO_EVENT user_event;

		// stops and waits for an event, note for a game loop this is probably not suitable,
		// investigate the other event handling functions at: http://www.allegro.cc/manual/5/events.html
		al_wait_for_event(event_queue, &user_event);

		// user can exit by pressing the Escape key or clicking the "x" icon to close the window
		if ((user_event.keyboard.keycode == ALLEGRO_KEY_ESCAPE) ||  (user_event.type == ALLEGRO_EVENT_DISPLAY_CLOSE))
			break;
	}
}

void cleanup(AllegroResources& resources)
{
	// clean up memory allocated for the font, event_queue and display
	al_destroy_font(resources.font);
	al_destroy_event_queue(resources.event_queue);
	al_destroy_display(resources.display);
}

int main(int argc, char **argv)
{
	// Note: this test code is written in a procedural programming style
	// NOT an object-oriented style.

	// AllegroResources consists of 3 pointer types, pointing to Allegro resources
	// that are dynamically allocated. Here, we initialise them all to 0 (NULL).
	// These resources are allocated during the allocateAllegroResources function,
	// and released in the cleanup function.
	AllegroResources resources = {0, 0, 0};

	SetupStatus status = setupAllegro(resources);
	if (status != OKAY)
	{
		reportSetupError(status);
		return -1;
	}

	drawOnDisplay(resources.font);
	waitForUserToExit(resources.event_queue);
	cleanup(resources);

	return 0;
}
